package com.csair.irp.job.file.parser.autogateway;

import com.csair.irp.job.file.exception.DataFileException;
import com.csair.irp.job.file.exception.DataOperaterException;
import com.csair.irp.job.file.exception.FileParseException;
import com.csair.irp.job.file.parser.base.BaseFileParser;
import com.csair.irp.job.file.parser.base.ParseResult;
import com.csair.irp.job.file.task.DispatchLogPO;
import com.csair.irp.job.file.parser.base.FileManagePO;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;

/**
 * 网关自动对接解析器
 *
 * @author liaolonglong
 */
@Component
public class AutoGatewayFileParser extends BaseFileParser {

    @Autowired
    private AutodockingRelationshipService autodockingRelationshipService;

    @Autowired
    private GatewayDataService gatewayDataService;

    /**
     * 默认行字符串转对象的转换器
     */
    private final AutoGatewayObjectConverter DEFAULT_LINE_CONVERTER = new DefaultAutoGatewayObjectConverter();

    private static final String KEY_BUSSINESS_NUMBER = "businessNumber";
    private static final String AUTO_USERNAME = "网关自动对接推送程序";

    /**
     * 先从数据库查找记录，如果有数据则直接使用数据库的对象，否则新建对象
     *
     * @param path
     * @param fileName
     * @param dispatchLog
     * @param params
     * @return
     */
    @Override
    protected FileManagePO getFileManage(String path, String fileName, DispatchLogPO dispatchLog, Map<Object, Object> params) throws DataFileException {
        FileManagePO fileManage = super.getFileManage(path, fileName, dispatchLog, params);

        if (fileManage == null) {
            fileManage = new FileManagePO();
            //设置文件信息
            fileManage.setCreateTime(new Date());
            fileManage.setFileName(fileName);
            fileManage.setDataDetail(fileName);
            fileManage.setFileSources(dispatchLog.getDataResourceTypeId());
            fileManage.setLink(path);
            fileManage.setUserName(AUTO_USERNAME);
            int settlementCycle = (Integer) params.get("settlementCycle");
            fileManage.setFileUploadDate(DateUtils.addDays(dispatchLog.getDispatchDate(), -settlementCycle));
        }
        //设置交易日期
        params.put("paymentDate", fileManage.getFileUploadDate());
        return fileManage;
    }

    @Override
    public String generateFileName(DispatchLogPO dispatchLog, Map<Object, Object> params) {

        //获取商户号
        String businessNumber = autodockingRelationshipService.getBusinessNumberByFileBaseinfoId(dispatchLog.getDataResourceTypeId());

        //如果商户号不存在直接返回
        if (StringUtils.isEmpty(businessNumber)) {
            return null;
        }


        String date = DateFormatUtils.format(dispatchLog.getDispatchDate(), "yyyyMMdd");

        //清算周期，设置空数据或异常数据文件交易日期
        int settlementCycle = autodockingRelationshipService.getGatewayBybusinessNumber(businessNumber).get(0).getSettlementCycle();
        //设置商户号和清算周期到参数
        params.put(KEY_BUSSINESS_NUMBER, businessNumber);
        params.put("settlementCycle", settlementCycle);
        return businessNumber + "-" + date + ".txt";
    }

    @Override
    protected boolean clearDataByFileId(Serializable fileId) throws DataFileException {
        return gatewayDataService.clearDataByFileId("file_id", (Long) fileId);
    }

    @Override
    protected void doParse(File file, Serializable fileId, Map<Object, Object> params) throws Exception {

        // 根据文件名获取商户号和推送日期
        String businessNumber = params.get(KEY_BUSSINESS_NUMBER).toString();

        //设置网关代码
        List<AutodockingRelationshipPO> codes = autodockingRelationshipService.list(null);

        Map<String, String> code = new HashMap<>();
        for (AutodockingRelationshipPO autodockingRelationship : codes) {
            code.put(autodockingRelationship.getUppGatewayCode() + "-" + autodockingRelationship.getBusinessNumber(), autodockingRelationship.getGatewayCode());
        }

        //解析文件获取数据
        Object[] data = readAutoGatewayFile(businessNumber, file, fileId.toString(), code, getAutoGatewayRejectFilter(), getAutoGatewayConverters(businessNumber), (Date) params.get("paymentDate"));
        log.info("解析文件的数据条数:" + data[3].toString());
        // 把解析的数据入库
        if (!CollectionUtils.isEmpty((List<GatewayDataPO>) data[0])) {//判断入库的数据是否为空
            if (!gatewayDataService.saveBatch((List<GatewayDataPO>) data[0], 300)) {
                throw new DataOperaterException("入库失败");
            }
        }

        //设置参数  把解析用到的参数传给其它解析步骤

        //总交易金额
        params.put("totalAmount", data[4]);
    }

    /**
     * 网关自动对接存在一个网关文件多个调度任务以及一个网关多个商户号的情况，因此重写更新调度任务状态的逻辑
     *
     * @param dispatchLog 调度任务
     * @param parseResult
     * @param file        数据文件
     * @param fileId      数据文件ID
     * @param params      解析过程传递的参数
     * @throws DataFileException
     */
    @Override
    protected void doParseAfter(DispatchLogPO dispatchLog, ParseResult parseResult, File file, Serializable fileId, Map<Object, Object> params) throws DataFileException {
        List<DispatchLogPO> relationTasks = null;
        //获取解析过程中传递过来的参数
        String businessNumber = params.get("businessNumber").toString();
        Date paymentDate = (Date) params.get("paymentDate");
        BigDecimal totalAmount = (BigDecimal) params.getOrDefault("totalAmount",BigDecimal.ZERO);
        String resourceIds = getResourceId(fileId, dispatchLog.getResourceIds());
        Long startTime = (Long) params.get(TASK_START_TIME_KEY);

        //更新调度任务解析状态
        //注意：一个网关有一个或多个商户号两种情况
        long dispatchTime = System.currentTimeMillis() - startTime;
        //获取该商户号对应的网关代码
        String gatewayCode = autodockingRelationshipService.getGatewayCodebyBusinessNumber(businessNumber);
        //获取与该商户号相同网关的所有商户号
        List<String> businessNumbers = autodockingRelationshipService.getBusinessNumbersByGatewayCode(gatewayCode);
        DispatchLogPO.StateEnum state = DispatchLogPO.StateEnum.ETL_STATE_RUN_SUCCESS;
        if (businessNumbers.size() > 1) {
            /** 一个网关对应多个商户号处理逻辑 */
            //计算所有已执行商户号的总交易金额
            BigDecimal countAmount = BigDecimal.ZERO;
            relationTasks = autodockingRelationshipService.getAutoGatewayDispatchLog(gatewayCode, paymentDate, DispatchLogPO.StateEnum.ETL_STATE_NO_RUN.getCode());
            if (!relationTasks.isEmpty() && relationTasks.get(0).getUpdateCount() != null) {
                countAmount = relationTasks.get(0).getUpdateCount();
            }
            totalAmount = countAmount.add(totalAmount);

            // 查询当天全部解析成功的文件
            List<String> results = autodockingRelationshipService.getGatewaySuccessInfoByGatewaycode(gatewayCode, paymentDate);
            if (businessNumbers.size() > results.size()) {
                // 更新对应支付网关所有调度日志状态
                state = DispatchLogPO.StateEnum.ETL_STATE_NO_RUN;
            }
        } else {
            /** 一个网关一个商户号处理逻辑 */
            relationTasks = autodockingRelationshipService.getAutoGatewayDispatchLog(gatewayCode, paymentDate, -1);
        }

        //设置关联调度任务需要更新的属性
        for (int i = 0; i < relationTasks.size(); i++) {
            DispatchLogPO task = relationTasks.get(i);
            //计算总执行时间
            task.setDispatchTime(dispatchTime + task.getDispatchTime());

            task.setResourceIds(resourceIds);
            task.setUpdateCount(totalAmount);
            task.setLogType(11);
            task.setEtlState(state.getCode());
        }
        parseResult.setRelationTasks(relationTasks);
    }

    /**
     * 设置网关数据拒绝器
     *
     * @return
     */
    private Map<String, AutoGatewayRejectFilter> getAutoGatewayRejectFilter() {

        Map<String, AutoGatewayRejectFilter> filters = new HashMap<>();
        // 政府采购过滤器 "008002" 不采集交易金额为0的数据
        List<AutodockingRelationshipPO> specialAutoGateway = autodockingRelationshipService.getGatewayBybusinessNumber("008002");
        for (int i = 0; i < specialAutoGateway.size(); i++) {
            filters.put(specialAutoGateway.get(i).getUppGatewayCode() + "-" + specialAutoGateway.get(i).getBusinessNumber(), new AutoGatewayRejectFilter() {
                @Override
                public boolean reject(String line, String[] datas) {
                    // 不采集交易金额为0的数据
                    char[] cs = datas[5].toCharArray();
                    for (char c : cs) {
                        if (',' != c && '.' != c && '0' != c) {
                            return false;
                        }
                    }
                    return true;
                }
            });
        }

        //GTG不采集GCP106的数据
        specialAutoGateway = autodockingRelationshipService.getGatewayBybusinessNumber("DKC");
        specialAutoGateway.addAll(autodockingRelationshipService.getGatewayByGatewaycode("236"));

        for (int i = 0; i < specialAutoGateway.size(); i++) {
            filters.put(specialAutoGateway.get(i).getUppGatewayCode() + "-" + specialAutoGateway.get(i).getBusinessNumber(), new AutoGatewayRejectFilter() {
                @Override
                public boolean reject(String line, String[] datas) {
                    //GTG不采集GCP106的数据
                    return datas[0].trim().startsWith("GCP106");
                }
            });
        }
        return filters;
    }


    /**
     * 获取文件解析器
     *
     * @param businessNumber 商户号
     * @return
     */
    private Map<String, AutoGatewayObjectConverter> getAutoGatewayConverters(final String businessNumber) {
        //网关自动对接文件解析器
        Map<String, AutoGatewayObjectConverter> parsers = new HashMap<>();

        //外卡自定义解析器
        List<AutodockingRelationshipPO> specialAutoGateway = autodockingRelationshipService.getGatewayByGatewaycode("CYBS");


        for (int i = 0; i < specialAutoGateway.size(); i++) {
            parsers.put(specialAutoGateway.get(i).getUppGatewayCode() + "-" + specialAutoGateway.get(i).getBusinessNumber(), new DefaultAutoGatewayObjectConverter() {
                @Override
                public GatewayDataPO parse(String line, String[] AutoGatewayDatas, String irpGatewayCode, String fileId) throws FileParseException {
                    GatewayDataPO gatewayData = super.parse(line, AutoGatewayDatas, irpGatewayCode, fileId);

                    //设置商户号
                    gatewayData.setMerchantCode(businessNumber);

                    //交易流水号以CYBS102开头的不对账
                    if (AutoGatewayDatas[0].trim().startsWith("CYBS102")) {
                        gatewayData.setCheckedPaymentData(-1);
                    }

                    //多推送的三个字段内容 申请ID|交易参考号|卡种
                    if (AutoGatewayDatas.length == 9) {
                        gatewayData.setApplyId(AutoGatewayDatas[6].trim());
                        gatewayData.setReferenceId(AutoGatewayDatas[7].trim());
                        gatewayData.setCardType(AutoGatewayDatas[8].trim());
                    }
                    return gatewayData;
                }
            });
        }
        return parsers;
    }

    /**
     * @param
     * @param map
     * @return Object[]
     * 0:网关数据对象,1:交易日期(改为从获取fileManage对象时设置交易日期),2:总交易单数,3:解析总交易单数，4:总交易金额(现金),5:总交易金额(非现金)
     * @throws Exception
     */
    public Object[] readAutoGatewayFile(String businessNumber, File file, String fileId, Map<String, String> map, Map<String, AutoGatewayRejectFilter> filters, Map<String, AutoGatewayObjectConverter> parsers, Date paymentDate) throws Exception {

        // 读取文件所有行记录
        List<String> lines = FileUtils.readLines(file, "UTF-8");

        //如果文件所有行数小于3行就认为是空文件
        if (lines.size() <= 3) {
            return new Object[]{new ArrayList<GatewayDataPO>(), null, 0, 0, BigDecimal.ZERO, BigDecimal.ZERO};
        }

        Object[] data = new Object[8];

        // 解析获取网关数据
        List<GatewayDataPO> pojos = new ArrayList<>();
        String mapKey, line;
        String[] temp, AutoGatewayDatas;
        AutoGatewayObjectConverter lineConverter = null;

        for (int i = 1, len = lines.size() - 2; i < len; i++) {
            line = lines.get(i);
            AutoGatewayDatas = line.split("\\|");

            // 判断文件内容是否出现空字符
            for (int j = 0; j <= 5; j++) {
                if (StringUtils.isEmpty(AutoGatewayDatas[j])) {
                    log.error("文件数据包含空字符串line={},index={}", line, j);
                    throw new FileParseException(FileManagePO.StateEnum.STATE_ERROR_CONTENT);
                } else {
                    AutoGatewayDatas[j] = AutoGatewayDatas[j].trim();
                }
            }
            // 根据UPP网关和商户号设置IRP网关，并把流水号中UPP网关改用IRP网关
            mapKey = getUppCode(AutoGatewayDatas[0]) + "-" + businessNumber;

            // 如果拒绝过滤器返回成功就不添加数据
            if (null != filters && null != filters.get(mapKey) && filters.get(mapKey).reject(line, AutoGatewayDatas)) {
                continue;
            }

            //调用解析器
            lineConverter = null != parsers && null != parsers.get(mapKey) ? parsers.get(mapKey) : DEFAULT_LINE_CONVERTER;

            if (i == 1) {
                lineConverter.initTotalAmount();
            }
            //通过文件解析器，将每一行数据解析为网关数据记录
            GatewayDataPO gatewayData = lineConverter.parse(line, AutoGatewayDatas, map.get(mapKey) == null ? mapKey : getUppCode(AutoGatewayDatas[0]), fileId);
            //设置该数据的清算日期
            gatewayData.setPaymentDate(paymentDate);
            pojos.add(gatewayData);

        }

        data[0] = pojos;

        // 设置统计信息 总交易单数和总交易金额
        String[] totalInfo = lines.get(lines.size() - 1).split("\\|");
        data[2] = Integer.valueOf(totalInfo[0]); // 文件总交易笔数
        data[3] = pojos.size(); // 解析总交易笔数
        data[4] = lineConverter == null ? BigDecimal.ZERO : lineConverter.getTotalAmount();
        if (totalInfo[1].contains(",")) {
            temp = totalInfo[1].split(",");
            // TODO data[4] = BigDecimal.valueOf(Double.parseDouble(temp[0]));
            data[5] = BigDecimal.valueOf(Double.parseDouble(temp[1]));
        } else {
            // TODO data[4] = BigDecimal.valueOf(Double.parseDouble(totalInfo[1]));
        }
        return data;
    }


    private String getUppCode(String paymentNumber) {
        char[] paymentNumberCharArray = paymentNumber.toCharArray();
        int index = 0;
        for (char temp : paymentNumberCharArray) {
            if (temp <= '9' && temp >= '0') {
                break;
            }
            index++;
        }
        // 如果流水号没有UPP网关情况，此数据为UPP推送异常
        if (index == paymentNumber.length()) {
            return "";
        }
        return paymentNumber.substring(0, index);
    }

}
